<script setup lang="ts">
import { useMouseInElement, usePointerSwipe } from '@vueuse/core'
import { useClamp, usePrecision } from '@vueuse/math'
import { computed, useTemplateRef, watch } from 'vue'

interface Props {
  min?: number
  max?: number
  showValues?: boolean
  precision?: number
}

const { min = 0, max = 100, showValues = true, precision = 0 } = defineProps<Props>()

const minValue = defineModel<number | ''>('minValue')
const maxValue = defineModel<number | ''>('maxValue')

// 计算百分比
const minPercent = computed(() => (((typeof minValue.value === 'number' ? minValue.value : min) - min) / (max - min)) * 100)
const maxPercent = computed(() => (((typeof maxValue.value === 'number' ? maxValue.value : max) - min) / (max - min)) * 100)

const sliderBar = useTemplateRef('sliderBar')
const sliderMin = useTemplateRef('sliderMin')
const sliderMax = useTemplateRef('sliderMax')

const { elementX, elementWidth } = useMouseInElement(sliderBar)

const mouseValue = useClamp(() => min + (elementX.value / elementWidth.value) * (max - min), min, max)
const precisedMouseValue = usePrecision(mouseValue, precision)

usePointerSwipe(sliderMin, {
  onSwipe: () => (minValue.value = Math.min(precisedMouseValue.value, typeof maxValue.value === 'number' ? maxValue.value : max))
})

usePointerSwipe(sliderMax, {
  onSwipe: () => (maxValue.value = Math.max(precisedMouseValue.value, typeof minValue.value === 'number' ? minValue.value : min))
})

const handleTrackClick = () => {
  const offsetToMin = mouseValue.value - (typeof minValue.value === 'number' ? minValue.value : min)
  const offsetToMax = mouseValue.value - (typeof maxValue.value === 'number' ? maxValue.value : max)
  const isCloserToMin = (Math.abs(offsetToMin) - Math.abs(offsetToMax) || offsetToMin) < 0
  isCloserToMin ? (minValue.value = precisedMouseValue.value) : (maxValue.value = precisedMouseValue.value)
}

watch(minValue, () => {
  if (minValue.value === '') minValue.value = undefined
  else if (typeof minValue.value === 'number' && minValue.value < min) minValue.value = min
  else if (typeof minValue.value === 'number' && minValue.value > max) minValue.value = max
})
watch(maxValue, () => {
  if (maxValue.value === '') maxValue.value = undefined
  else if (typeof maxValue.value === 'number' && maxValue.value > max) maxValue.value = max
  else if (typeof maxValue.value === 'number' && maxValue.value < min) maxValue.value = min
})
</script>

<template>
  <div class="px-2.5 select-none">
    <div ref="sliderBar" class="relative h-8 cursor-pointer" @click="handleTrackClick">
      <!-- 滑轨背景 -->
      <div class="bg-base-300 absolute top-1/2 right-0 left-0 h-2 -translate-y-1/2 rounded-full shadow-inner"></div>
      s
      <!-- 活动区域 -->
      <div
        class="from-primary to-secondary pointer-events-none absolute top-1/2 h-2 -translate-y-1/2 rounded-full bg-linear-to-r shadow-sm transition-all ease-[cubic-bezier(0.25,0.46,0.45,0.94)]"
        :style="{
          left: `${minPercent}%`,
          right: `${100 - maxPercent}%`
        }"
      ></div>

      <!-- 滑块 -->
      <div
        ref="sliderMin"
        class="border-primary absolute top-1/2 z-10 size-5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 bg-white transition-all ease-[cubic-bezier(0.25,0.46,0.45,0.94)] select-none hover:scale-110"
        :style="{ left: `${minPercent}%` }"
        @click.stop
      >
        <div class="bg-primary absolute inset-1 rounded-full"></div>
      </div>
      <div
        ref="sliderMax"
        class="border-primary absolute top-1/2 z-10 size-5 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 bg-white transition-all ease-[cubic-bezier(0.25,0.46,0.45,0.94)] select-none hover:scale-110"
        :style="{ left: `${maxPercent}%` }"
        @click.stop
      >
        <div class="bg-primary absolute inset-1 rounded-full"></div>
      </div>
    </div>

    <div v-if="showValues" class="-mx-1.5 mt-2 flex items-center justify-between text-xs text-white">
      <span>{{ min }}</span>
      <span>{{ max }}</span>
    </div>
  </div>
</template>
